#include "CogProductionPool.h"
#include "CogEvolver.h"

// ------------------------------------------------------------------------------------------

CogProductionPool::CogProductionPool(uint32_t _id, CogProductionPool* _pParentPool)
: poolId(_id),
  pParentGrammar(NULL),
  pParentPool(_pParentPool),
  maxId(0),
  validInstances(0),
  currentInstanceUsage(0)
{
}

bool CogProductionPool::hasInstances() const 
{ 
	if (pParentGrammar->pEvolver->parameters.buildMode & COGBM_PARENTAL)
	{
		return getValidInstanceUsage() > 0;
	}
	else
	{
		return getCurrentInstanceUsage() > 0; 
	}
}

CogProductionPool& CogProductionPool::addNewPool(uint32_t id)
{
	pools.push_back(CogProductionPool(id, this));
	pools.back().setGrammar(pParentGrammar);
	return pools.back();
}

CogProductionPool* CogProductionPool::getPool(uint32_t id)
{
	list<CogProductionPool>::iterator pool_iter = pools.begin();
	while (pool_iter != pools.end())
	{
		if (pool_iter->poolId == id)
		{
			return &*pool_iter;
		}
		++pool_iter;
	}
	return NULL;
}

CogProduction& CogProductionPool::addNewInstance(uint32_t id, uint32_t generation, bool initCache)
{
	instances.push_back(CogProduction(id, COGSF_NONE, this, NULL, generation));
	instances.back().lhs.pRule = &instances.back();
	if (initCache)
	{
		instances.back().pCache = pParentGrammar->cache.allocate();
	}
	return instances.back();
}

CogProduction& CogProductionPool::duplicateInstance(const CogProduction& original, uint32_t generation)
{
	CogProduction& ruleCopy = addNewInstance(generation, ((original.lhs.flag & COGSF_EVAL) && pParentGrammar->cache.hasStorage()) ? true : false);
	ruleCopy.lhs.flag = original.lhs.flag & ~COGSF_STARTING & ~COGSF_TEMPLATE;
	ruleCopy.rhs = original.rhs;
	ruleCopy.ntSize = original.ntSize;
	return ruleCopy;
}

void CogProductionPool::addInitialRule(const CogProduction& original)
{
	if (original.lhs.flag & COGSF_TEMPLATE)
	{
		CogProduction& newrule = addNewTemplate(original.lhs.id);
		newrule.lhs.flag = original.lhs.flag;
		newrule.rhs = original.rhs;
		newrule.ntSize = original.ntSize;
		newrule.usedBy = original.usedBy;
	}
	else
	{
		CogProduction& newrule = addNewInstance(original.lhs.id, 0, ((original.lhs.flag & COGSF_EVAL) && pParentGrammar->cache.hasStorage()) ? true : false);
		newrule.lhs.flag = original.lhs.flag;
		newrule.rhs = original.rhs;
		newrule.ntSize = original.ntSize;
		newrule.use(original.usedBy);
	}
}

void CogProductionPool::computeInstanceUsage()
{
	if (pParentGrammar->pEvolver->parameters.buildMode & COGBM_PARENTAL)
	{
		validInstances = instances.size();
		totalInstanceUsage = currentInstanceUsage;
		list<CogProductionPool>::iterator pool_iter = pools.begin();
		while (pool_iter != pools.end())
		{
			pool_iter->computeInstanceUsage();
			++pool_iter;
		}
	}

	/*
	totalInstanceUsage = 0;
	list<CogProduction>::iterator rule_iter = instances.begin();
	while (rule_iter != instances.end())
	{
		totalInstanceUsage += rule_iter->usedBy;
		++rule_iter;
	}
	list<CogProductionPool>::iterator pool_iter = pools.begin();
	while (pool_iter != pools.end())
	{
		pool_iter->computeInstanceUsage();
		totalInstanceUsage += pool_iter->getInstanceUsage();
		++pool_iter;
	}
	if (totalInstanceUsage != currentInstanceUsage)
	{
		currentInstanceUsage = currentInstanceUsage;
	}*/
}

void CogProductionPool::computeTemplateUsage()
{
	totalTemplateUsage = 0;
	list<CogProduction>::iterator temp_iter = templates.begin();
	while (temp_iter != templates.end())
	{
		totalTemplateUsage += temp_iter->usedBy;
		++temp_iter;
	}
	list<CogProductionPool>::iterator pool_iter = pools.begin();
	while (pool_iter != pools.end())
	{
		pool_iter->computeTemplateUsage();
		totalTemplateUsage += pool_iter->getTemplateUsage();
		++pool_iter;
	}
}

void CogProductionPool::clearAll()
{
	clearInstances();
	clearTemplates();
	clearEmptyPools();
	clearLabels();
}

void CogProductionPool::clearInstances()
{
	// instances
	list<CogProduction>::iterator rem_iter = instances.begin();
	while (rem_iter != instances.end())
	{
		if ((rem_iter->lhs.flag & COGSF_EVAL) && pParentGrammar->cache.hasStorage())
		{
			pParentGrammar->cache.destroy(rem_iter->pCache);
		}
		rem_iter = instances.erase(rem_iter);
	}
	// pools
	list<CogProductionPool>::iterator pool_iter = pools.begin();
	while (pool_iter != pools.end())
	{
		pool_iter->clearInstances();
		++pool_iter;
	}
	// reset usage
	totalInstanceUsage = currentInstanceUsage = 0;
}

void CogProductionPool::clearTemplates()
{
	// templates
	templates.clear();
	// pools
	list<CogProductionPool>::iterator pool_iter = pools.begin();
	while (pool_iter != pools.end())
	{
		pool_iter->clearTemplates();
		++pool_iter;
	}
	// reset usage
	totalTemplateUsage = 0;
}

void CogProductionPool::clearEmptyPools()
{
	// pools
	list<CogProductionPool>::iterator rem_iter = pools.begin();
	while (rem_iter != pools.end())
	{
		rem_iter->clearEmptyPools();
		if (rem_iter->isEmpty())
		{
			rem_iter = pools.erase(rem_iter);
		}
		else
		{
			++rem_iter;
		}
	}
}


void CogProductionPool::setGrammar(CogGrammar* pGram) 
{ 
	pParentGrammar = pGram; 
	list<CogProductionPool>::iterator pool_iter = pools.begin();
	while (pool_iter != pools.end())
	{
		pool_iter->setGrammar(pGram);
		++pool_iter;
	}
}

size_t CogProductionPool::getInstanceSize() const 
{ 
	size_t totalSize = instances.size();
	list<CogProductionPool>::const_iterator pool_iter = pools.begin();
	while (pool_iter != pools.end())
	{
		totalSize += pool_iter->getInstanceSize();
		++pool_iter;
	}
	return totalSize;
}

size_t CogProductionPool::getTemplateSize() const 
{ 
	size_t totalSize = templates.size();
	list<CogProductionPool>::const_iterator pool_iter = pools.begin();
	while (pool_iter != pools.end())
	{
		totalSize += pool_iter->getTemplateSize();
		++pool_iter;
	}
	return totalSize;
}

CogProduction* CogProductionPool::getFirstLocalInstanceOfId(uint32_t id)
{
	list<CogProduction>::iterator first_iter = instances.begin();
	for (; first_iter != instances.end(); ++first_iter)
	{
		if (first_iter->lhs.id == id)
		{
			return &*first_iter;
		}
	}
	#ifdef _DEBUG
	globalMessageLog.print(COGMT_ERROR_SYSTEM, VERBOSITY_NONE, "Trying to get first production with ID %d from production pool %d, but pool is empty", id, poolId);
	#endif
	return NULL;
}

CogProduction* CogProductionPool::getFirstLocalInstanceWithRHS(vector<CogSymbol>& rhs)
{
	list<CogProduction>::iterator first_iter = instances.begin();
	for (; first_iter != instances.end(); ++first_iter)
	{
		bool same = true;
		vector<CogSymbol>::iterator rhs1_iter = first_iter->rhs.begin();
		vector<CogSymbol>::iterator rhs2_iter = rhs.begin();
		for (; rhs2_iter != rhs.end(); ++rhs2_iter)
		{
			if ((rhs1_iter == first_iter->rhs.end()) || (rhs1_iter->id != rhs2_iter->id))
			{
				same = false;
				break;
			}
			else
			{
				++rhs1_iter;
			}
		}
		if (same && (rhs1_iter == first_iter->rhs.end()))
		{
			return &*first_iter;
		}
	}
	#ifdef _DEBUG
	globalMessageLog.print(COGMT_INFO_SYSTEM, VERBOSITY_HIGH, "Trying to get first production with same RHS from production pool %d, but none found", poolId);
	#endif
	return NULL;
}

CogProduction* CogProductionPool::getFirstLocalTemplate()
{
	if (templates.size())
	{
		return &templates.front();
	}
	else
	{
		#ifdef _DEBUG
		globalMessageLog.print(COGMT_ERROR_SYSTEM, VERBOSITY_NONE, "Trying to get a template from production pool %d, but pool is empty", poolId);
		#endif
		return NULL;
	}
}

CogProduction* CogProductionPool::getAnyInstance()
{
	if (pParentGrammar->pEvolver->parameters.buildMode & COGBM_PARENTAL)
	{
		return getAnyValidInstance(cogRandomIndex(totalInstanceUsage));
	}
	else
	{
		return getAnyInstance(cogRandomIndex(currentInstanceUsage));
	}
}

CogProduction* CogProductionPool::getAnyInstance(uint32_t target)
{
	list<CogProduction>::iterator any_iter = instances.begin();
	while (any_iter != instances.end())
	{
		if (any_iter->usedBy > target)
		{
			return &*any_iter;
		}
		else
		{
			target -= any_iter->usedBy;
		}
		++any_iter;
	}
	list<CogProductionPool>::iterator pool_iter = pools.begin();
	while (pool_iter != pools.end())
	{
		if (target < pool_iter->getCurrentInstanceUsage())
		{
			target -= pool_iter->getCurrentInstanceUsage();
		}
		else
		{
			return pool_iter->getAnyInstance(target);
		}
		++pool_iter;
	}
	#ifdef _DEBUG
	globalMessageLog.print(COGMT_ERROR_SYSTEM, VERBOSITY_NONE, "Trying to get random production from production pool %d, but not enough productions in pool", poolId);
	#endif
	return NULL;
}

CogProduction* CogProductionPool::getAnyValidInstance(uint32_t target)
{
	list<CogProduction>::iterator any_iter = instances.begin();
	for (size_t i = 0; i < validInstances; i++)
	{
		if (any_iter->usedBy > target)
		{
			return &*any_iter;
		}
		else
		{
			target -= any_iter->usedBy;
		}
		++any_iter;
	}
	list<CogProductionPool>::iterator pool_iter = pools.begin();
	while (pool_iter != pools.end())
	{
		if (target < pool_iter->getValidInstanceUsage())
		{
			target -= pool_iter->getValidInstanceUsage();
		}
		else
		{
			return pool_iter->getAnyValidInstance(target);
		}
		++pool_iter;
	}
	#ifdef _DEBUG
	globalMessageLog.print(COGMT_ERROR_SYSTEM, VERBOSITY_NONE, "Trying to get random production from production pool %d, but not enough productions in pool", poolId);
	#endif
	return NULL;
}

CogProduction* CogProductionPool::getAnyTemplate()
{
	return getAnyTemplate(cogRandomIndex(getTemplateUsage()));
}

CogProduction* CogProductionPool::getAnyTemplate(uint32_t target)
{
	list<CogProduction>::iterator any_iter = templates.begin();
	while (any_iter != templates.end())
	{
		if (any_iter->usedBy > target)
		{
			return &*any_iter;
		}
		else
		{
			target -= any_iter->usedBy;
		}
		++any_iter;
	}
	list<CogProductionPool>::iterator pool_iter = pools.begin();
	while (pool_iter != pools.end())
	{
		if (target < pool_iter->getTemplateUsage())
		{
			target -= pool_iter->getTemplateUsage();
		}
		else
		{
			return pool_iter->getAnyTemplate(target);
		}
		++pool_iter;
	}
	#ifdef _DEBUG
	globalMessageLog.print(COGMT_ERROR_SYSTEM, VERBOSITY_NONE, "Trying to get random template from production pool %d, but not enough templates in pool", poolId);
	#endif
	return NULL;
}

void CogProductionPool::removeUnusedInstances()
{
	list<CogProduction>::iterator rem_iter = instances.begin();
	while (rem_iter != instances.end())
	{
		if (!rem_iter->usedBy && !(rem_iter->lhs.flag & COGSF_CONST))
		{
			if ((rem_iter->lhs.flag & COGSF_EVAL) && pParentGrammar->cache.hasStorage())
			{
				pParentGrammar->cache.destroy(rem_iter->pCache);
			}
			rem_iter = instances.erase(rem_iter);
		}
		else
		{
			++rem_iter;
		}
	}
	list<CogProductionPool>::iterator pool_iter = pools.begin();
	while (pool_iter != pools.end())
	{
		pool_iter->removeUnusedInstances();
		++pool_iter;
	}
}

size_t CogProductionPool::buildInstances()
{
	if (pParentGrammar && pParentGrammar->pEvolver)
	{
		size_t errors = 0;
		list<CogProduction>::iterator inst_iter = instances.begin();
		for (; inst_iter != instances.end(); ++inst_iter)
		{
			if (build(*inst_iter))
			{
				if (inst_iter->lhs.flag & COGSF_STARTING)
				{
					pParentGrammar->pEvolver->addSolution(&*inst_iter);
					inst_iter->use();
				}
			}
			else
			{
				errors++;
			}
		}
		list<CogProductionPool>::iterator pool_iter = pools.begin();
		for (; pool_iter != pools.end(); ++pool_iter)
		{
			errors += pool_iter->buildInstances();
		}
		return errors;
	}
	else
	{
		return instances.size();
	}
}

size_t CogProductionPool::buildTemplates()
{
	if (pParentGrammar)
	{
		size_t errors = 0;
		list<CogProduction>::iterator temp_iter = templates.begin();
		for (; temp_iter != templates.end(); ++temp_iter)
		{
			if (build(*temp_iter))
			{
				if (temp_iter->lhs.flag & COGSF_STARTING)
				{
					pParentGrammar->startingTemplates.push_back(&*temp_iter);
				}
			}
			else
			{
				errors++;
			}
		}
		list<CogProductionPool>::iterator pool_iter = pools.begin();
		for (; pool_iter != pools.end(); ++pool_iter)
		{
			errors += pool_iter->buildTemplates();
		}
		return errors;
	}
	else
	{
		return templates.size();
	}
}

bool CogProductionPool::build(CogProduction& rule)
{
	// find production pointers for RHS symbols
	vector<CogSymbol>::iterator rhs_iter = rule.rhs.begin();
	while (rhs_iter != rule.rhs.end())
	{
		if (!(rhs_iter->flag & COGSF_TERMINAL))
		{
			if (rhs_iter->flag & COGSF_TEMPLATE)
			{
				if (!rhs_iter->pPool->getFirstLocalTemplate())
				{
					globalMessageLog.print(COGMT_ERROR_IO, VERBOSITY_NONE, "Cannot link pointer: RHS symbol \"%s\" does not denote a template pool", rhs_iter->pPool->getParent()->getLabel(rhs_iter->pPool->getId()).c_str());
					return false;
				}
			}
			else
			{
				CogProduction* pFound = rhs_iter->pPool->getFirstLocalInstanceOfId(rhs_iter->id);
				if (pFound)
				{
					pFound->use();
					rhs_iter->pRule = pFound;
					rhs_iter->flag |= pFound->lhs.flag & COGSF_EVAL;
				}
				else
				{
					globalMessageLog.print(COGMT_ERROR_IO, VERBOSITY_NONE, "Cannot link pointer: RHS symbol \"%s\" has no known instance", rhs_iter->pPool->getLabel(rhs_iter->id).c_str());
					return false;
				}
			}
		}
		++rhs_iter;
	}
	return true;
}

void CogProductionPool::markConstTemplates()
{
	list<CogProduction>::iterator temp_iter = templates.begin();
	for (; temp_iter != templates.end(); ++temp_iter)
	{
		if (!temp_iter->lhs.pRule) // should be NULL unless CONST pointer assigned
		{
			if (temp_iter->lhs.flag & COGSF_CONST)
			{
				// search for an existing instance with the same RHS
				CogProduction* pFound = getFirstLocalInstanceWithRHS(temp_iter->rhs);
				// if not found, create a new singular instance
				if (!pFound)
				{
					pFound = &duplicateInstance(*temp_iter, 0);
					pFound->ntSize = 1;
				}
				// pointer to instance
				temp_iter->lhs.pRule = pFound;
			}		
		}
	}
	list<CogProductionPool>::iterator pool_iter = pools.begin();
	for (; pool_iter != pools.end(); ++pool_iter)
	{
		pool_iter->markConstTemplates();
	}
}

// ----- symbol label management -----

uint32_t CogProductionPool::addLabel(const string& label)
{
	labelMap[++maxId] = label;
	return maxId;
}

void CogProductionPool::clearId()
{
	maxId = 0;
	map<uint32_t, string>::const_iterator lmit = labelMap.begin();
	for (; lmit != labelMap.end(); ++lmit)
	{
		if (maxId > lmit->first)
		{
			maxId = lmit->first;
		}
	}
	// pools
	list<CogProductionPool>::iterator pool_iter = pools.begin();
	while (pool_iter != pools.end())
	{
		pool_iter->clearId();
		++pool_iter;
	}
}

void CogProductionPool::clearLabels() 
{ 
	maxId = 0; 
	labelMap.clear(); 
	// pools
	list<CogProductionPool>::iterator pool_iter = pools.begin();
	while (pool_iter != pools.end())
	{
		pool_iter->clearLabels();
		++pool_iter;
	}
}

bool CogProductionPool::getId(const string& label, uint32_t& id) const
{
	map<uint32_t, string>::const_iterator lmit = labelMap.begin();
	for (; lmit != labelMap.end(); ++lmit)
	{
		if (lmit->second == label)
		{
			id = lmit->first;
			return true;
		}
	}
	return false;
}

string CogProductionPool::getLabel(uint32_t id) const
{
	map<uint32_t, string>::const_iterator lmit = labelMap.find(id);
	if (lmit == labelMap.end())
	{
		// returns numerical ID if UD not found
		stringstream ss;
		ss << id;
		return ss.str();
	}
	else
	{
		return lmit->second;
	}
}

// ----- from/to stream parsing -----

void CogProductionPool::ancestryToStream(uint32_t childId, ostream &out) const
{
	if (pParentPool)
	{
		pParentPool->ancestryToStream(poolId, out);
	}
	if (childId)
	{
		out << "<" << getLabel(childId).c_str();
	}
}

void CogProductionPool::instancesToStream(ostream &out, const CogGrammar* pGram) const
{
	list<CogProduction>::const_iterator inst_iter = instances.begin();
	for (; inst_iter != instances.end(); ++inst_iter)
	{
		inst_iter->toStream(out, pGram);
	}
	list<CogProductionPool>::const_iterator pool_iter = pools.begin();
	for (; pool_iter != pools.end(); ++pool_iter)
	{
		pool_iter->instancesToStream(out, pGram);
	}
}

void CogProductionPool::templatesToStream(ostream &out, const CogGrammar* pGram) const
{
	list<CogProduction>::const_iterator temp_iter = templates.begin();
	for (; temp_iter != templates.end(); ++temp_iter)
	{
		temp_iter->toStream(out, pGram);
	}
	list<CogProductionPool>::const_iterator pool_iter = pools.begin();
	for (; pool_iter != pools.end(); ++pool_iter)
	{
		pool_iter->templatesToStream(out, pGram);
	}
}

void CogProductionPool::poolsToStream(ostream &out) const
{
	list<CogProductionPool>::const_iterator pool_iter = pools.begin();
	for (; pool_iter != pools.end(); ++pool_iter)
	{
		out << "% *";
		ancestryToStream(0, out);
		out << endl;
		pool_iter->poolsToStream(out);
	}
}

// ------------------------------------------------------------------------------------------